from pml.Parser import public
from pml.Parser import Variable
from pml.Parser import Expression
from pml.Parser import For
from pml.Parser import If
from pml.Parser import Function
from pml.Parser import Module

class AST(public.Node):
    def __init__(self, body, depth):
        super().__init__('ast', None, None, depth)
        self.body = body
    def __repr__(self):
        body = f'['
        for i in self.body:
            body += '\n' + '\t' * (self.depth + 1) + i.__repr__()
        body += '\n' + '\t' * self.depth + ']'

        return 'AST({})(\n{}body({})={}\n{})'.format(self.depth, '\t' * self.depth, self.depth + 1, body, '\t' * (self.depth - 1))

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        return self

    @staticmethod
    def tp_init(frame, this, position):
        pass

class ASTBuilder(public.ASTBuilder):
    def __init__(self, tokens=None, depth=0, inStructure=False):
        super().__init__('AST', tokens, depth)
        self.inStructure = inStructure

    def getObject(self):
        self.methods['__init__'] = self.tp_init

        self.methods['buildAST'] = self.buildAST

        return self

    @staticmethod
    def buildAST(frame, this: 'ASTBuilder', position):
        body = []

        while True:
            # print(this.token)
            if this.token.type == public.TokenConstants.TT_KEYWORD:
                if this.token.value == public.TokenConstants.Keywords.END:
                    if not this.inStructure:
                        # error
                        pass
                    return public.Objects.object.PMTypeObject(AST(public.PML.object.ChangeObject.PMLList_FromList(body), depth=this.depth).getObject())

                elif this.token.value == public.TokenConstants.Keywords.VAR or this.token.value == public.TokenConstants.Keywords.LET or this.token.value == public.TokenConstants.Keywords.CONST:
                    builder = Variable.VariableBuilder(this.tokens, depth=this.depth + 2)
                    body.append(builder.buildVariable(frame, builder, position))

                elif this.token.value == public.TokenConstants.Keywords.FOR:
                    builder = For.ForBuilder(this.tokens, depth=this.depth + 2)
                    body.append(builder.buildFor(frame, builder, position))

                elif this.token.value == public.TokenConstants.Keywords.IF:
                    builder = If.IfBuilder(this.tokens, depth=this.depth + 2)
                    body.append(builder.buildIf(frame, builder, position))

                elif this.token.value == public.TokenConstants.Keywords.FUNCTION:
                    builder = Function.FunctionBuilder(this.tokens, depth=this.depth + 2)
                    body.append(builder.buildFunction(frame, builder, position))

                elif this.token.value == public.TokenConstants.Keywords.IMPORT or this.token.value == public.TokenConstants.Keywords.INCLUDE:
                    builder = Module.ModuleBuilder(this.tokens, depth=this.depth + 2)
                    body.append(builder.buildModule(frame, builder, position))

            elif this.token.type == public.TokenConstants.TT_OPERATION:
                if this.token.value == public.TokenConstants.TV_ADD or this.token.value == public.TokenConstants.TV_SUB:
                    builder = Expression.ExpressionBuilder(this.tokens, depth=this.depth + 2)
                    body.append(builder.buildExpression(frame, builder, position))

            elif this.token.type == public.TokenConstants.TT_INT or this.token.type == public.TokenConstants.TT_FLOAT or \
                this.token.type == public.TokenConstants.TT_STRING or this.token.type == public.TokenConstants.TT_BOOLEAN or \
                this.token.type == public.TokenConstants.TT_CHAR or this.token.type == public.TokenConstants.TT_NULL or this.token.type == public.TokenConstants.TT_NAME:
                builder = Expression.ExpressionBuilder(this.tokens, depth=this.depth + 2)
                body.append(builder.buildExpression(frame, builder, position))

            try:
                this.updateToken()
            except StopIteration:
                break
        return public.Objects.object.PMTypeObject(AST(public.PML.object.ChangeObject.PMLList_FromList(body), depth=this.depth).getObject())

    @staticmethod
    def tp_init(frame, this, position):
        pass
